#include "Mesh.h"
#include "Game.h"
#include "Bone.h"

#include <vector>
#include <string>

#include "TexturesManager.h"
#include "ShadowMap.h"
#include "DepthEffect.h"

Mesh::Mesh()
{
	m_vertexBuffer = 0;
	m_indexBuffer = 0;
	mBone = NULL;
	mOrigine = XMFLOAT3(0,0,0);
}


Mesh::Mesh(const Mesh& other)
{
}

Mesh Mesh::operator=(Mesh const&)
{
	return Mesh();
}

Mesh::~Mesh()
{
}

void Mesh::Dispose()
{
}

bool Mesh::Initialize(const aiMesh* paiMesh, Texture* pTexture, BasicEffect* pShader)
{
	mDeviceContext = CDirect3D::Instance()->GetDeviceContext();

	VertexTypeTexture* vertices;
	unsigned long* indices;
	D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc;
    D3D11_SUBRESOURCE_DATA vertexData, indexData;
	HRESULT result;

	mShader = pShader;
	mTexture = pTexture;

	mName = std::string(paiMesh->mName.C_Str());

	// Set the number of vertices in the vertex array.
	m_vertexCount = paiMesh->mNumVertices;

	// Set the number of indices in the index array.
	m_indexCount = paiMesh->mNumFaces * 3;

	// Create the vertex array.
	vertices = new VertexTypeTexture[m_vertexCount];
	if(!vertices)
	{
		return false;
	}
	double x =0,y =0,z =0; 
	int i;
	// Create the index array.
	for(i = 0;i < m_vertexCount;i++)
	{
		x += paiMesh->mVertices[i].x;
		y += paiMesh->mVertices[i].y;
		z += paiMesh->mVertices[i].z;
		vertices[i].position = XMFLOAT3(paiMesh->mVertices[i].x, paiMesh->mVertices[i].y, paiMesh->mVertices[i].z); 
		vertices[i].texture = XMFLOAT2(paiMesh->mTextureCoords[0][i].x, paiMesh->mTextureCoords[0][i].y);
		vertices[i].normal = XMFLOAT3(paiMesh->mNormals[i].x, paiMesh->mNormals[i].y, paiMesh->mNormals[i].z);  // Bottom left.
		vertices[i].alpha = 1;

		mVertices.push_back(vertices[i].position);
	}
	x /= i; y /= i; z /= i;
	mCenter = XMFLOAT3((float)x, (float)y, (float)z);

	std::vector<unsigned long> vIndices;

	unsigned int totalindices = 0;
	for(unsigned int i = 0;i < paiMesh->mNumFaces;i++)
	{
		for(unsigned int  j = 0; j < paiMesh->mFaces[i].mNumIndices;j++)
		{
			unsigned int index = paiMesh->mFaces[i].mIndices[j];
			vIndices.push_back(index);
			totalindices++;
		}
	}

	m_indexCount = vIndices.size();
	indices = new unsigned long[m_indexCount];
	if(!indices)
	{
		return false;
	}
	std::copy(vIndices.begin(), vIndices.end(), indices);

	// Set up the description of the static vertex buffer.
    vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    vertexBufferDesc.ByteWidth = sizeof(VertexTypeTexture) * m_vertexCount;
    vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    vertexBufferDesc.CPUAccessFlags = 0;
    vertexBufferDesc.MiscFlags = 0;
	vertexBufferDesc.StructureByteStride = 0;

	// Give the subresource structure a pointer to the vertex data.
    vertexData.pSysMem = vertices;
	vertexData.SysMemPitch = 0;
	vertexData.SysMemSlicePitch = 0;

	// Now create the vertex buffer.
    result = CDirect3D::Instance()->GetDevice()->CreateBuffer(&vertexBufferDesc, &vertexData, &m_vertexBuffer);
	if(FAILED(result))
	{
		return false;
	}

	// Set up the description of the static index buffer.
    indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    indexBufferDesc.ByteWidth = sizeof(unsigned long) * m_indexCount;
    indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    indexBufferDesc.CPUAccessFlags = 0;
    indexBufferDesc.MiscFlags = 0;
	indexBufferDesc.StructureByteStride = 0;

	// Give the subresource structure a pointer to the index data.
    indexData.pSysMem = indices;
	indexData.SysMemPitch = 0;
	indexData.SysMemSlicePitch = 0;

	// Create the index buffer.
	result = CDirect3D::Instance()->GetDevice()->CreateBuffer(&indexBufferDesc, &indexData, &m_indexBuffer);
	if(FAILED(result))
	{
		return false;
	}

	// Release the arrays now that the vertex and index buffers have been created and loaded.
	delete [] vertices;
	vertices = 0;

	delete [] indices;
	indices = 0;

	return true;
}

XMFLOAT3 Mesh::GetCenter()
{
	return mCenter;
}

void Mesh::Render(const XMFLOAT4X4 &worldMatrix, const XMFLOAT4X4 &viewMatrix, const XMFLOAT4X4 &projectionMatrix)
{	
	IASetVertices();
	IASetIndices();
	IASetTexture();

	mShader->SetTransformsMatrices(worldMatrix, viewMatrix, projectionMatrix);
	mShader->Render(m_indexCount);
}

void Mesh::IASetVertices()
{
	unsigned int stride = sizeof(VertexTypeTexture); 
	unsigned int offset = 0;
	
	mDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	mDeviceContext->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &offset);
}

void Mesh::IASetIndices()
{
	mDeviceContext->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R32_UINT, 0);
}

int Mesh::GetIndicesCount()
{
	return m_indexCount;
}

void Mesh::IASetTexture()
{
	mShader->SetTexture(mTexture);
}

//void Mesh::ShadowRender(ShadowMap* pShadowMap, const XMFLOAT4X4 &worldMatrix)
//{
//
//	XMFLOAT4X4* viewMatrix;
//	XMFLOAT4X4* projectionMatrix;
//
//
//	unsigned int stride;
//	unsigned int offset;
//
//	stride = sizeof(VertexTypeTexture); 
//	offset = 0;
//
//	ID3D11DeviceContext * deviceContext = CDirect3D::Instance()->GetDeviceContext();
//
//	deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
//	deviceContext->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &offset);
//	deviceContext->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R32_UINT, 0);
//
//	mShader->SetTexture(mTexture);
//	pShadowMap->GetTransformMatrix(&viewMatrix, &projectionMatrix);
//	//mShader->SetTransformsMatrices(worldMatrix, *viewMatrix, *projectionMatrix);
//	//mShader->Render(m_indexCount);
//	Game::Instance()->GetDepthShader()->SetTransformsMatrices(worldMatrix, *viewMatrix, *projectionMatrix);
//	Game::Instance()->GetDepthShader()->Render(m_indexCount);
//}

void Mesh::GetBoundingBox(BoundingBox &pBoundingBox, XMFLOAT4X4 &pTransform)
{
	XMFLOAT3 tranformedVector;

	XMMATRIX lTransform = XMLoadFloat4x4(&pTransform);

	XMVECTOR vTranformedVector = XMVector3Transform(XMLoadFloat3(&(*mVertices.begin())), lTransform);
	XMStoreFloat3(&tranformedVector, vTranformedVector);

	pBoundingBox.x1 = pBoundingBox.x2 = tranformedVector.x;
	pBoundingBox.y1 = pBoundingBox.y2 = tranformedVector.y;
	pBoundingBox.z1 = pBoundingBox.z2 = tranformedVector.z;

	for(std::vector<XMFLOAT3>::iterator mVertex = mVertices.begin();mVertex != mVertices.end();++mVertex)
	{
		vTranformedVector = XMVector3Transform(XMLoadFloat3(&(*mVertex)), lTransform);
		XMStoreFloat3(&tranformedVector, vTranformedVector);

		pBoundingBox.x1 = min(pBoundingBox.x1,tranformedVector.x);
		pBoundingBox.y1 = min(pBoundingBox.y1,tranformedVector.y);
		pBoundingBox.z1 = min(pBoundingBox.z1,tranformedVector.z);

		pBoundingBox.x2 = max(pBoundingBox.x2,tranformedVector.x);
		pBoundingBox.y2 = max(pBoundingBox.y2,tranformedVector.y);
		pBoundingBox.z2 = max(pBoundingBox.z2,tranformedVector.z);
	}
}

bool Mesh::IsTextureTransparent()
{
	return mTexture->IsTransparent();
}

std::string Mesh::GetName()
{
	return mName;
}

int Mesh::GetIndex()
{
	return mIndex;
}

void Mesh::SetIndex(int pIndex)
{
	mIndex = pIndex;
}

void Mesh::SetBone(Bone* pBone)
{
	mBone = pBone;
}

Bone* Mesh::GetBone()
{
	return mBone;
}

void Mesh::SetOrigine(XMFLOAT3 &pOrigine)
{
	mOrigine = pOrigine;
}

XMFLOAT3* Mesh::GetOrigine()
{
	return &mOrigine;
}
